/*
 * This file referenced Lionel Brits & Jeff Molofee's openGL Tutorials,The tutorials get from http://nehe.gamedev.net/
 */
#define GL_GLEXT_PROTOTYPES

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <stdarg.h>

#define IMG_W 256
#define IMG_H 256

#define PARTICLE_COUNT 1000

GLboolean filled;			// done filling in the grid?
GLboolean gameover;			// is the game over?

int delay;					// enemy delay
int adjust = 3;				// speed adjustment for really slow video cards
int live = 5;				// player lives
int level = 1;				// internal game level
int level2 = 1;				// displayed game level
int stage = 1;				// game stage

struct object{
	int fx,fy;				// fine movement position
	int x,y;				// current player position
	float spin;				// spin direction
};

struct object player;
struct object enemy[9];
struct object hourglass;

GLuint texture[2];			// storage for particle textures
GLuint base;				// base display list for the font

GLubyte *loadBmp(const char *filename,unsigned int w,unsigned int h)
{
	unsigned char *buf;
	int fd = open(filename,O_RDONLY);
	if(fd < 0){
		printf("open file failed,errno:%s\n",strerror(errno));
		return NULL;
	}
	unsigned long buf_count = w * h * 3 * sizeof(unsigned char);
	buf = (unsigned char *)malloc(buf_count);
	if(!buf){
		printf("alloc memery failed,errno:%s\n",strerror(errno));
		close(fd);
		return NULL;
	}
	lseek(fd,54,SEEK_SET);
	if(read(fd,buf,buf_count) <= 0){
		printf("read file failed,errno:%s\n",strerror(errno));
		close(fd);
		free(buf);
		return NULL;
	}
	close(fd);
	return buf;
}

void create_font(void)
{
	int i;
	base = glGenLists(256);
	glBindTexture(GL_TEXTURE_2D,texture[0]);
	for(i = 0;i < 256;i++){
		float cx = (float)(i % 16) / 16.0f;				// x position of current character
		float cy = (float)(i / 16) / 16.0f;				// y position of current character

		glNewList(base + i,GL_COMPILE);
			glBegin(GL_QUADS);
				glTexCoord2f(cx,1.0f - cy - 0.0625f);	// 0.0625 is 16/256,one charater width/height
				glVertex2d(0,16);
				glTexCoord2f(cx + 0.0625f,1.0f - cy - 0.625f);
				glVertex2d(16,16);
				glTexCoord2f(cx + 0.0625f,1.0f - cy);
				glVertex2d(16,0);
				glTexCoord2f(cx,1.0f - cy);
				glVertex2d(0,0);
			glEnd();
			glTranslated(15,0,0);						// move to the right of the character
		glEndList();
	}
}

void free_cont(void)
{
	glDeleteLists(base,256);
}

void print_string(GLint x,GLint y,unsigned int set,const char *fmt,...)
{
	char text[512];
	va_list ap;
	if(!fmt){
		return;
	}
	va_start(ap,fmt);
		vsprintf(text,fmt,ap);
	va_end(ap);
	set = set > 1 ? 1 : set;

	glEnable(GL_TEXTURE_2D);
	glLoadIdentity();
	glTranslated(x,y,0);
	glListBase(base - 32 + (128 * set));				// -32 means skip special ascii 0--31
	if(0 == set){
		glScalef(1.5f,2.0f,1.0f);
	}
	glCallLists(strlen(text),GL_UNSIGNED_BYTE,text);
	glDisable(GL_TEXTURE_2D);
}

void init(void)
{
	GLubyte *fonts = loadBmp("fonts.bmp",IMG_W,IMG_H);
	GLubyte *bg = loadBmp("game_bg.bmp",IMG_W,IMG_H);

	glGenTextures(2,&texture[0]);						// Create Three Textures

	glBindTexture(GL_TEXTURE_2D,texture[0]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D,0,3,IMG_W,IMG_H,0,GL_RGB,GL_UNSIGNED_BYTE,fonts);
	glBindTexture(GL_TEXTURE_2D,texture[1]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D,0,3,IMG_W,IMG_H,0,GL_RGB,GL_UNSIGNED_BYTE,bg);

	free(fonts);
	free(bg);

	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f,0.0f,0.0f,0.5f);
	glClearDepth(1.0f);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);					// set line antialiasing

	create_font();
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	print_string(10,10,1,"aaaaaaaaa");

	glFlush();
}

void reset_display(void)
{
	glutIdleFunc(NULL);

	glutSwapBuffers();
}

void spin_display(void)
{
	glutPostRedisplay();
}

void reshape(int w, int h)
{
	glViewport(0,0,w,h);								// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	glOrtho(0.0f,w,h,0.0f,-1.0f,1.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

void keyboard (unsigned char key, int x, int y)
{
	switch (key){
		case 'a':
		case 'A':
			glutIdleFunc(spin_display);
			break;
		case 's':
		case 'S':
			glutIdleFunc(NULL);
			break;
		case 'r':
		case 'R':
			reset_display();
			break;
		case '8':
			break;
		case '2':
			break;
		case '4':
			break;
		case '6':
			break;
		case 27:
			exit(0);
			break;
		default:
			return;
	}
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMainLoop();
	return 0;
}
